Startup

Get reference alleles

reference_alleles <- read_tsv("GCh38_reference_alleles.txt", col_names = F) %>% pull(1)
Rows: 26 Columns: 1
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (1): X1

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
genome_region_reference <- get_allele_length(reference_alleles)

Get arcasHLA alignment stats

arcas_log_dir <- sprintf("%s/arcasHLA", isb_path)
alignment_stats_df <- hla_mapping_stats_import(isb_samples, arcas_log_dir)

Import cell counts

cell_counts_df <- readRDS("isb_sequenced_cell_count.RDS")

Assemble accuracy DF

success_df <- readRDS("isb_success.RDS")
accuracy_df <- readRDS("isb_accuracy_drb345_filtered.RDS")
accuracy_df <- accuracy_df %>% 
  left_join(
    success_df %>% select(sample:field, genotyper, success = accuracy),
    by = c("sample", "locus", "field", "genotyper")
  )%>% 
  left_join(alignment_stats_df, by = c("sample", "locus")) %>% 
  left_join(genome_region_reference, by = "locus") %>%
  left_join(cell_counts_df, by = "sample") %>%
  mutate_at(vars(c(reads, bp)), as.numeric) %>% 
  mutate(coverage = (reads * 91)/bp) %>% 
  mutate(n_alleles = map_dbl(allele, function(x) sum(!is.na(x)))) %>% 
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)$", sample) & genotyper != "hlaminer") 

Coverage based plots

gg_coverage(accuracy_df, x_var = "coverage", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

gg_coverage(accuracy_df, x_var = "coverage", y_var = "success")
`geom_smooth()` using formula 'y ~ x'

gg_coverage(accuracy_df, x_var = "coverage", y_var = "n_alleles", facet_genotyper = T)

Cell number based plots

gg_coverage(accuracy_df, x_var = "n_cells", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

gg_coverage(accuracy_df, x_var = "n_cells", y_var = "success")
`geom_smooth()` using formula 'y ~ x'

gg_coverage(accuracy_df, x_var = "n_cells", y_var = "n_alleles", facet_genotyper = T)

Subsample READS

Import read subsample data

# Import predicted genotypes
subsample_reads_path <- "/labs/khatrilab/solomonb/covid/isb/subsample"
subsample_reads_samples <- tibble(sample = list.files(sprintf("%s/arcasHLA", subsample_reads_path))) %>% 
  filter(grepl("^INCOV", sample)) %>% 
  separate(sample, into = c("sample"), sep = "\\.", extra = "drop") %>% 
  distinct() %>% 
  pull(sample)
subsample_reads_df <- format_hla_table(combine_HLA_import(path = subsample_reads_path, samples = subsample_reads_samples))
subsample_reads_df <- subsample_reads_df %>% 
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)_", sample))
subsample_reads_df
# Expand invitro genotypes across read subsample names
invitro_df <- invitro_import()
Rows: 1557 Columns: 9
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (8): Sample ID, Locus, Allele 1, Allele 2, Comments, Diploid Ambiguities, Allele 1 Ambiguities, Allele 2 Ambiguities
dbl (1): index

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
invitro_reads_df <- subsample_reads_df %>% 
  select(sample) %>% 
  distinct() %>% 
  separate(sample, into = c("sample", "subsample"), sep = "_") %>% 
  left_join(invitro_df, by = "sample") %>% 
  unite(sample, sample, subsample, sep = "_") %>% 
  format_hla_table() %>% 
  drop_na()
invitro_reads_df
# Import read statistics
subsample_reads_arcas_log_dir <- sprintf("%s/arcasHLA", subsample_reads_path)
subsample_reads_alignment_stats_df <- hla_mapping_stats_import(subsample_reads_samples, subsample_reads_arcas_log_dir)

Calculate accuracy

# Calculate accuracy
subsample_reads_accuracy_df <- compare_hla(
  hla_df = bind_rows(
    subsample_reads_df,
    invitro_reads_df
  ),
  reference = "invitro", 
  method = "accuracy"
)
# saveRDS(subsample_reads_accuracy_df, "isb_subsample_reads_accuracy.RDS")
# Calculate success
subsample_reads_success_df <- compare_hla(
  hla_df = bind_rows(
    subsample_reads_df,
    invitro_reads_df
  ),
  reference = "invitro", 
  method = "success"
)
# saveRDS(subsample_reads_success_df, "isb_subsample_reads_success.RDS")
# Combine various data
subsample_reads_combined_df <- subsample_reads_accuracy_df %>% 
  left_join(
    subsample_reads_success_df %>% select(sample:field, genotyper, success = accuracy),
    by = c("sample", "locus", "field", "genotyper")
  )%>% 
  left_join(subsample_reads_alignment_stats_df, by = c("sample", "locus")) %>% 
  left_join(genome_region_reference, by = "locus") %>%
  bind_rows(accuracy_df %>% select(-n_cells)) %>% # No cell numbers for read subsamples
  mutate_at(vars(c(reads, bp)), as.numeric) %>% 
  mutate(coverage = (reads * 91)/bp) %>%  # Calculate coverage
  mutate(n_alleles = map_dbl(allele, function(x) sum(!is.na(x)))) %>%  # Calculate n_alleles
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)", sample) & genotyper != "hlaminer") 

Reads plots

gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

plt_read_accuracy_all <- gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "accuracy") 
plt_read_accuracy_abc <- gg_coverage(subsample_reads_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "reads", y_var = "accuracy") 
gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "success")
`geom_smooth()` using formula 'y ~ x'

plt_read_success_all <- gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "success")
plt_read_success_abc <- gg_coverage(subsample_reads_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "reads", y_var = "success") 
gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "n_alleles", facet_genotyper = T)

plt_read_allele_all <- gg_coverage(subsample_reads_combined_df, x_var = "reads", y_var = "n_alleles", facet_genotyper = T)
plt_read_allele_abc <- gg_coverage(subsample_reads_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "reads", y_var = "n_alleles", facet_genotyper = T)
  • Default arcasHLA cut off 75 reads (log10 = 1.8)
subsample_reads_combined_df %>% 
  filter(field == "field_2") %>% 
  mutate(n_alleles = factor(n_alleles, levels = 0:2)) %>% 
  ggplot(aes(y = n_alleles, x = log10(coverage)))+
  # geom_violin(scale = "count", alpha = 0.5, )+
  geom_jitter(size = 0.2, width = 0, height =0.2, alpha = 0.2)+
  # geom_boxplot(width=0.1)+
  # ggbeeswarm::geom_beeswarm(size = 0.1, alpha = 0.1)+
  # coord_flip()+
  facet_grid(genotyper ~ locus, scales = "free")+
  theme_bw()

Subsample CELLS

# Import predicted genotypes
subsample_cells_path <- "/labs/khatrilab/solomonb/covid/isb/subsample_cells"
subsample_cells_samples <- tibble(sample = list.files(sprintf("%s/arcasHLA", subsample_cells_path))) %>% 
  filter(grepl("^INCOV", sample)) %>% 
  separate(sample, into = c("sample"), sep = "\\.", extra = "drop") %>% 
  distinct() %>% 
  pull(sample)
subsample_cells_df <- format_hla_table(combine_HLA_import(path = subsample_cells_path, samples = subsample_cells_samples))
subsample_cells_df <- subsample_cells_df %>% 
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)_", sample))
subsample_cells_df
# Expand invitro genotypes across read subsample names
invitro_df <- invitro_import()
Rows: 1557 Columns: 9
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (8): Sample ID, Locus, Allele 1, Allele 2, Comments, Diploid Ambiguities, Allele 1 Ambiguities, Allele 2 Ambiguities
dbl (1): index

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
invitro_cells_df <- subsample_cells_df %>% 
  select(sample) %>% 
  distinct() %>% 
  separate(sample, into = c("sample", "subsample"), sep = "_") %>% 
  left_join(invitro_df, by = "sample") %>% 
  unite(sample, sample, subsample, sep = "_") %>% 
  format_hla_table() %>% 
  drop_na()
invitro_cells_df
# Import read statistics
subsample_cells_arcas_log_dir <- sprintf("%s/arcasHLA", subsample_cells_path)
subsample_cells_alignment_stats_df <- hla_mapping_stats_import(subsample_cells_samples, subsample_cells_arcas_log_dir)
# Import cell counts for each subsample
subsample_cell_counts_df <- tibble(line = system("wc -l /labs/khatrilab/solomonb/covid/isb/subsample_cells/barcodes/INCOV*", intern = T)) %>% 
  separate(line, into = c("n_cells", "file"), sep = " /") %>% 
  drop_na() %>% 
  mutate(sample = gsub("\\..*","",basename(file))) %>% 
  mutate(n_cells = as.numeric(n_cells)) %>% 
  select(-file) %>% 
  # separate(sample, into = c("sample", "subset"), sep = "_") %>% 
  bind_rows(cell_counts_df)
Warning: Expected 2 pieces. Missing pieces filled with `NA` in 1 rows [883].
subsample_cell_counts_df

Calculate accuracy

# Calculate accuracy
subsample_cells_accuracy_df <- compare_hla(
  hla_df = bind_rows(
    subsample_cells_df,
    invitro_cells_df
  ),
  reference = "invitro", 
  method = "accuracy"
)
# saveRDS(subsample_cells_accuracy_df, "isb_subsample_cells_accuracy.RDS")
# subsample_cells_accuracy_df <- readRDS("isb_subsample_cells_accuracy.RDS")
# Calculate success
subsample_cells_success_df <- compare_hla(
  hla_df = bind_rows(
    subsample_cells_df,
    invitro_cells_df
  ),
  reference = "invitro", 
  method = "success"
)
# saveRDS(subsample_cells_success_df, "isb_subsample_cells_success.RDS")
subsample_cells_success_df <- readRDS("isb_subsample_cells_success.RDS")
# Combine various data
subsample_cells_combined_df <- subsample_cells_accuracy_df %>% 
  left_join(
    subsample_cells_success_df %>% select(sample:field, genotyper, success = accuracy),
    by = c("sample", "locus", "field", "genotyper")
  )%>% 
  left_join(subsample_cells_alignment_stats_df, by = c("sample", "locus")) %>% 
  left_join(genome_region_reference, by = "locus") %>%
  left_join(subsample_cell_counts_df, by = "sample") %>%
  bind_rows(accuracy_df) %>% 
  mutate_at(vars(c(reads, bp)), as.numeric) %>% 
  mutate(coverage = (reads * 91)/bp) %>% 
  mutate(n_alleles = map_dbl(allele, function(x) sum(!is.na(x)))) %>% 
  filter(grepl("^[ABC]|D.[AB]1", locus) & grepl("-(AC|BL)", sample) & genotyper != "hlaminer") 

Coverage plots

gg_coverage(subsample_cells_combined_df, x_var = "coverage", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

gg_coverage(subsample_cells_combined_df, x_var = "coverage", y_var = "success")
`geom_smooth()` using formula 'y ~ x'

gg_coverage(subsample_cells_combined_df, x_var = "coverage", y_var = "n_alleles", facet_genotyper = T)

Cell number plots

gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "accuracy") 
`geom_smooth()` using formula 'y ~ x'

plt_cell_accuracy_all <- gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "accuracy") 
plt_cell_accuracy_abc <- gg_coverage(subsample_cells_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "n_cells", y_var = "accuracy") 
gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "success") 
`geom_smooth()` using formula 'y ~ x'

plt_cell_success_all <- gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "success") 
plt_cell_success_abc <- gg_coverage(subsample_cells_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "n_cells", y_var = "success") 
gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "n_alleles", facet_genotyper = T)

plt_cell_allele_all <- gg_coverage(subsample_cells_combined_df, x_var = "n_cells", y_var = "n_alleles", facet_genotyper = T)
plt_cell_allele_abc <- gg_coverage(subsample_cells_combined_df %>% filter(grepl("^[ABC]", locus)), x_var = "n_cells", y_var = "n_alleles", facet_genotyper = T)
# subsample_cells_combined_df %>% 
#   mutate(reads = reads/n_cells) %>% 
#   filter(reads < 100) %>% 
#   gg_coverage(x_var = "reads", y_var = "accuracy", x_log = F) 

Combined plots

no_leg <- list(theme(legend.position = "none"))
legend <- cowplot::get_legend(plt_read_accuracy_abc)
`geom_smooth()` using formula 'y ~ x'
plot_grid(
  plot_grid(
    plt_read_accuracy_abc + no_leg,
    plt_read_success_abc + no_leg,
    plt_read_allele_abc,
    ncol = 1,
    align = "v",
    axis = "lr",
    rel_heights = c(1,1,1.2),
    labels = LETTERS[1:3]
  ),
  plot_grid(
    plt_cell_accuracy_abc + no_leg,
    plt_cell_success_abc + no_leg,
    plt_cell_allele_abc,
    ncol = 1,
    align = "v",
    axis = "lr",
    rel_heights = c(1,1,1.2),
    labels = LETTERS[4:6]
  ),
  plot_grid(
    legend,
    legend,
    NA,
    ncol = 1
  ),
  nrow = 1,
  rel_widths = c(1,1,0.25)
)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

no_leg <- list(theme(legend.position = "none"))
legend <- cowplot::get_legend(plt_read_accuracy_all)
`geom_smooth()` using formula 'y ~ x'
plot_grid(
  plot_grid(
    plt_read_accuracy_all + no_leg,
    plt_read_success_all + no_leg,
    plt_read_allele_all,
    ncol = 1,
    align = "v",
    axis = "lr",
    rel_heights = c(1,1,1.2),
    labels = LETTERS[1:3]
  ),
  plot_grid(
    plt_cell_accuracy_all + no_leg,
    plt_cell_success_all + no_leg,
    plt_cell_allele_all,
    ncol = 1,
    align = "v",
    axis = "lr",
    rel_heights = c(1,1,1.2),
    labels = LETTERS[4:6]
  ),
  plot_grid(
    legend,
    legend,
    NA,
    ncol = 1
  ),
  nrow = 1,
  rel_widths = c(1,1,0.25)
)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

Main plot

loci <- c("A", "B", "C", "DPB1", "DQA1", "DRB1")
plt_read_accuracy_subset <- gg_coverage(
  subsample_reads_combined_df %>% 
    filter(locus %in% loci) %>% 
    filter(!(genotyper == "phlat" & locus == "DPB1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DPB1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DQA1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DRB1")), 
  x_var = "reads", y_var = "accuracy") +
  facet_wrap(~locus, nrow = 2)
plt_read_accuracy_subset
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 59 rows containing non-finite values (stat_smooth).
Warning: Removed 59 rows containing missing values (geom_point).

save_plot("figures_pdf/v2/5_coverage.pdf", plt_read_accuracy_subset, base_height = 4, base_width = 6)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 59 rows containing non-finite values (stat_smooth).
Warning: Removed 59 rows containing missing values (geom_point).
plt_read_allele_all <- gg_coverage(subsample_reads_combined_df %>% 
    filter(locus %in% loci) %>% 
    filter(!(genotyper == "phlat" & locus == "DPB1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DPB1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DQA1")) %>% 
    filter(!(genotyper == "optitype" & locus == "DRB1")), x_var = "reads", y_var = "n_alleles", facet_genotyper = T)
plt_read_allele_all
Warning: Removed 62 rows containing missing values (geom_point).

plt_main <- plot_grid(
  plt_read_accuracy_subset,
  plt_read_allele_all,
  ncol = 1,
  rel_heights = c(3,2),
  align = "v",
  axis = "lr",
  labels = c("A", "B")
)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 59 rows containing non-finite values (stat_smooth).
Warning: Removed 59 rows containing missing values (geom_point).
Warning: Removed 62 rows containing missing values (geom_point).
plt_main

save_plot("figures_pdf/v2/5_coverage.pdf", plt_main, base_height = 7, base_width = 7)

Slope stats

  # browser()
  df <- df %>% 
    filter(field == field_var) %>% 
    exclude_genotyper_fields() # Replace w/ exclude_genotyper_fields?
Error in UseMethod("filter") : 
  no applicable method for 'filter' applied to an object of class "function"
model_stats <- bind_rows(
  get_lm_stats(subsample_reads_combined_df, x_var = "reads", y_var = "accuracy", x_log = T) %>% mutate(x_var = "reads", y_var = "accuracy"),
  get_lm_stats(subsample_reads_combined_df, x_var = "reads", y_var = "success", x_log = T) %>% mutate(x_var = "reads", y_var = "success"),
  get_lm_stats(subsample_cells_combined_df, x_var = "n_cells", y_var = "accuracy", x_log = T) %>% mutate(x_var = "cells", y_var = "accuracy"),
  get_lm_stats(subsample_cells_combined_df, x_var = "n_cells", y_var = "success", x_log = T) %>% mutate(x_var = "cells", y_var = "success")
)  %>% 
  # filter(grepl("^[ABC]", locus)) %>% 
  ungroup() %>% 
  filter(field == "field_2") 

Table

col_vars <- c("x_var", "locus")
row_vars <- c("y_var", "genotyper")

flex_df <- model_stats %>% 
  # filter(grepl("^[ABC]", locus)) %>% 
  ungroup() %>% 
  filter(field == "field_2") %>% 
  mutate(CI = sprintf("(%s) - (%s)", est_min, est_max)) %>% 
  mutate(genotyper = reformat_hla_genotyper(genotyper)) %>% 
  select(genotyper, locus, CI, x_var, y_var) %>% 
  pivot_wider(names_from = col_vars, values_from = "CI", names_sep =  "-") 
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(col_vars)` instead of `col_vars` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
df_key <- tibble(col_keys = names(flex_df)) %>% 
  filter(!(col_keys %in% c(col_vars, row_vars))) %>% 
  separate(col_keys, into = col_vars, sep = "-", remove = F) %>%
  mutate_at(vars(contains("genotyper")), reformat_hla_genotyper) %>% 
  arrange_at(col_vars) %>% 
  mutate_all(as.character) %>% 
  mutate_at(col_vars[!(col_vars %in% "locus")], str_to_sentence)

flex_df %>% 
  select(row_vars, everything()) %>% 
  mutate_at(row_vars[!(row_vars %in% "genotyper")], str_to_sentence) %>% 
  # select(locus, df_key$col_keys) %>% 
  flextable() %>% 
  colformat_char(na_str = "---") %>%
  merge_v(j=1:2) %>% 
  set_header_df(mapping = df_key, key = "col_keys") %>% 
  merge_h(part = "header") %>%
  theme_vanilla() %>% 
  # vline(j=vlines_sequence, border = fp_border_default()) %>%
  fix_border_issues() %>% 
  autofit() %>% 
  fontsize(size = 8, part = "all") %>% 
  print(preview = "pptx")
Note: Using an external vector in selections is ambiguous.
ℹ Use `all_of(row_vars)` instead of `row_vars` to silence this message.
ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
  df <- df %>% 
    mutate(field = reformat_hla_field(field)) %>% 
    mutate_at(vars(contains("genotyper")), reformat_hla_genotyper) %>% 
    mutate(cell_value = ifelse(!is.na(se), 
                               sprintf("%s %s %s", round(mean_accuracy,2),"\u00B1",round(se,2)),
                               sprintf("%s", round(mean_accuracy,2)))) %>%
    mutate(cell_value = ifelse(grepl("NA", cell_value), NA, cell_value)) %>% 
    select(-sd,-se, -mean_accuracy) %>% 
    pivot_wider(names_from = nesting_vars, values_from = "cell_value", names_sep = "-") 
Error: Problem with `mutate()` column `field`.
ℹ `field = reformat_hla_field(field)`.
x object 'field' not found
Run `rlang::last_error()` to see where the error occurred.

Scratch work

subsample_cells_combined_df %>% 
  filter(field == "field_2") %>% 
  mutate(n_alleles = factor(n_alleles, levels = 0:2)) %>% 
  ggplot(aes(x = n_alleles, y = log10(n_cells)))+
  geom_violin(scale = "width")+
  geom_jitter(size = 0.2, height = 0, width =0.05, alpha = 0.2)+
  # geom_boxplot(width=0.1)+
  # ggbeeswarm::geom_beeswarm(size = 0.1, alpha = 0.1)+
  coord_flip()+
  facet_grid(genotyper ~ locus, scales = "free")+
  theme_bw()

Coverage stats

subsample_cells_combined_df %>% 
  ggplot(aes(x = n_cells, y = coverage, color = genotyper)) + 
  geom_point() +
  facet_wrap(genotyper ~ locus, scales = "free", nrow = 3) + 
  theme_bw() +
  theme(strip.text = element_blank(),
        axis.text.x = element_text(angle = 45, hjust = 1))+
  scale_color_brewer(palette = "Dark2")
subsample_cells_combined_df %>% 
  select(-genotyper) %>% distinct() %>% 
  mutate(cov_per_cell = coverage/n_cells) %>% 
  ggplot(aes(x = log10(n_cells), y = cov_per_cell)) + 
  geom_point(color = "black", alpha = 0.1) +
  stat_smooth(method = "lm")+
  # stat_smooth()+
    facet_wrap( ~ locus, scales = "free", nrow = 1) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))+
  scale_color_brewer(palette = "Dark2")
subsample_cells_combined_df %>% 
  select(-genotyper) %>% distinct() %>% 
  filter(!is.na(n_cells)) %>% 
  mutate(cov_per_cell = coverage/n_cells) %>% 
  mutate(n_cells = cut(n_cells, seq(0,10000,by=2500), right = F)) %>% 
  ggplot(aes(x = n_cells, y = cov_per_cell)) + 
  stat_summary(fun = function(x) mean(x,na.rm=T), geom = "bar") +
  # geom_point(color = "black", alpha = 0.1) +
  # stat_smooth(method = "lm")+
  # stat_smooth()+
  facet_wrap( ~ locus, scales = "free", nrow = 1) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))+
  scale_color_brewer(palette = "Dark2")
hist(cut(1:100, seq(1,100,10)))
subsample_cells_combined_df %>% 
  ungroup() %>% 
  count(genotyper, locus, field)
subsample_reads_combined_df %>% 
  ungroup() %>% 
  count(genotyper, locus, field)
LS0tCnRpdGxlOiAiMykgQ292ZXJhZ2UgQW5hbHlzaXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KCgojIFN0YXJ0dXAgCgpgYGB7ciwgbWVzc2FnZSA9IEZ9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZ2dwbWlzYykKCnNvdXJjZSgiZGF0YV9pbXBvcnRfZnVuY3Rpb25zLlIiKQpzb3VyY2UoImNhbGN1bGF0aW9uX2Z1bmN0aW9ucy5SIikKc291cmNlKCJmaWd1cmVfZm9ybWF0X2Z1bmN0aW9ucy5SIikKYWxsX2hsYV9leHBhbmRlZCA8LSByZWFkUkRTKCJhbGxfaGxhX2V4cGFuZGVkLlJEUyIpCmlzYl9wYXRoIDwtICIvbGFicy9raGF0cmlsYWIvc29sb21vbmIvY292aWQvaXNiIgppc2Jfc2FtcGxlcyA8LSByZWFkX3RzdigiL2xhYnMva2hhdHJpbGFiL3NvbG9tb25iL2NvdmlkL2lzYi9sb2dzLzIxMDIxN18yMzI3MjUvcGFyYWxsZWwubG9nIikgJT4lIAogIHNlcGFyYXRlKENvbW1hbmQsIGludG8gPSBjKE5BLCAic2FtcGxlIiksIHNlcCA9ICIgIikgJT4lIAogIHB1bGwoc2FtcGxlKSAlPiUgdW5pcXVlKCkKYGBgCgojIyMgR2V0IHJlZmVyZW5jZSBhbGxlbGVzCgotIEdDaDM4IHJlZmVyZW5jZSBhbGxlbGVzIChwZXIgaHR0cHM6Ly93d3cuZWJpLmFjLnVrL2lwZC9pbWd0L2hsYS9oZWxwL2dlbm9taWNzLmh0bWwpCi0gVG8gYmUgdXNlZCB0byBjYWxjdWxhdGUgY292ZXJhZ2UKCmBgYHtyLCBtZXNzYWdlID0gRn0KcmVmZXJlbmNlX2FsbGVsZXMgPC0gcmVhZF90c3YoIkdDaDM4X3JlZmVyZW5jZV9hbGxlbGVzLnR4dCIsIGNvbF9uYW1lcyA9IEYpICU+JSBwdWxsKDEpCmdlbm9tZV9yZWdpb25fcmVmZXJlbmNlIDwtIGdldF9hbGxlbGVfbGVuZ3RoKHJlZmVyZW5jZV9hbGxlbGVzKQpgYGAKCiMjIyBHZXQgYXJjYXNITEEgYWxpZ25tZW50IHN0YXRzCgpgYGB7cn0KYXJjYXNfbG9nX2RpciA8LSBzcHJpbnRmKCIlcy9hcmNhc0hMQSIsIGlzYl9wYXRoKQphbGlnbm1lbnRfc3RhdHNfZGYgPC0gaGxhX21hcHBpbmdfc3RhdHNfaW1wb3J0KGlzYl9zYW1wbGVzLCBhcmNhc19sb2dfZGlyKQpgYGAKCiMjIyBJbXBvcnQgY2VsbCBjb3VudHMKCmBgYHtyfQpjZWxsX2NvdW50c19kZiA8LSByZWFkUkRTKCJpc2Jfc2VxdWVuY2VkX2NlbGxfY291bnQuUkRTIikKYGBgCgojIyMgQXNzZW1ibGUgYWNjdXJhY3kgREYKCmBgYHtyfQpzdWNjZXNzX2RmIDwtIHJlYWRSRFMoImlzYl9zdWNjZXNzLlJEUyIpCmFjY3VyYWN5X2RmIDwtIHJlYWRSRFMoImlzYl9hY2N1cmFjeV9kcmIzNDVfZmlsdGVyZWQuUkRTIikKYWNjdXJhY3lfZGYgPC0gYWNjdXJhY3lfZGYgJT4lIAogIGxlZnRfam9pbigKICAgIHN1Y2Nlc3NfZGYgJT4lIHNlbGVjdChzYW1wbGU6ZmllbGQsIGdlbm90eXBlciwgc3VjY2VzcyA9IGFjY3VyYWN5KSwKICAgIGJ5ID0gYygic2FtcGxlIiwgImxvY3VzIiwgImZpZWxkIiwgImdlbm90eXBlciIpCiAgKSU+JSAKICBsZWZ0X2pvaW4oYWxpZ25tZW50X3N0YXRzX2RmLCBieSA9IGMoInNhbXBsZSIsICJsb2N1cyIpKSAlPiUgCiAgbGVmdF9qb2luKGdlbm9tZV9yZWdpb25fcmVmZXJlbmNlLCBieSA9ICJsb2N1cyIpICU+JQogIGxlZnRfam9pbihjZWxsX2NvdW50c19kZiwgYnkgPSAic2FtcGxlIikgJT4lCiAgbXV0YXRlX2F0KHZhcnMoYyhyZWFkcywgYnApKSwgYXMubnVtZXJpYykgJT4lIAogIG11dGF0ZShjb3ZlcmFnZSA9IChyZWFkcyAqIDkxKS9icCkgJT4lIAogIG11dGF0ZShuX2FsbGVsZXMgPSBtYXBfZGJsKGFsbGVsZSwgZnVuY3Rpb24oeCkgc3VtKCFpcy5uYSh4KSkpKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCJeW0FCQ118RC5bQUJdMSIsIGxvY3VzKSAmIGdyZXBsKCItKEFDfEJMKSQiLCBzYW1wbGUpICYgZ2Vub3R5cGVyICE9ICJobGFtaW5lciIpIApgYGAKCgojIyMgQ292ZXJhZ2UgYmFzZWQgcGxvdHMKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mi41LCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKGFjY3VyYWN5X2RmLCB4X3ZhciA9ICJjb3ZlcmFnZSIsIHlfdmFyID0gImFjY3VyYWN5IikgCmBgYApgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTIuNSwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShhY2N1cmFjeV9kZiwgeF92YXIgPSAiY292ZXJhZ2UiLCB5X3ZhciA9ICJzdWNjZXNzIikKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShhY2N1cmFjeV9kZiwgeF92YXIgPSAiY292ZXJhZ2UiLCB5X3ZhciA9ICJuX2FsbGVsZXMiLCBmYWNldF9nZW5vdHlwZXIgPSBUKQpgYGAKCiMjIyBDZWxsIG51bWJlciBiYXNlZCBwbG90cwoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0yLjUsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KZ2dfY292ZXJhZ2UoYWNjdXJhY3lfZGYsIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpIApgYGAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0yLjUsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KZ2dfY292ZXJhZ2UoYWNjdXJhY3lfZGYsIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJzdWNjZXNzIikKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShhY2N1cmFjeV9kZiwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCmBgYAoKCiMgU3Vic2FtcGxlIFJFQURTCgojIyMgSW1wb3J0IHJlYWQgc3Vic2FtcGxlIGRhdGEKCmBgYHtyfQojIEltcG9ydCBwcmVkaWN0ZWQgZ2Vub3R5cGVzCnN1YnNhbXBsZV9yZWFkc19wYXRoIDwtICIvbGFicy9raGF0cmlsYWIvc29sb21vbmIvY292aWQvaXNiL3N1YnNhbXBsZSIKc3Vic2FtcGxlX3JlYWRzX3NhbXBsZXMgPC0gdGliYmxlKHNhbXBsZSA9IGxpc3QuZmlsZXMoc3ByaW50ZigiJXMvYXJjYXNITEEiLCBzdWJzYW1wbGVfcmVhZHNfcGF0aCkpKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCJeSU5DT1YiLCBzYW1wbGUpKSAlPiUgCiAgc2VwYXJhdGUoc2FtcGxlLCBpbnRvID0gYygic2FtcGxlIiksIHNlcCA9ICJcXC4iLCBleHRyYSA9ICJkcm9wIikgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHB1bGwoc2FtcGxlKQpzdWJzYW1wbGVfcmVhZHNfZGYgPC0gZm9ybWF0X2hsYV90YWJsZShjb21iaW5lX0hMQV9pbXBvcnQocGF0aCA9IHN1YnNhbXBsZV9yZWFkc19wYXRoLCBzYW1wbGVzID0gc3Vic2FtcGxlX3JlYWRzX3NhbXBsZXMpKQpzdWJzYW1wbGVfcmVhZHNfZGYgPC0gc3Vic2FtcGxlX3JlYWRzX2RmICU+JSAKICBmaWx0ZXIoZ3JlcGwoIl5bQUJDXXxELltBQl0xIiwgbG9jdXMpICYgZ3JlcGwoIi0oQUN8QkwpXyIsIHNhbXBsZSkpCnN1YnNhbXBsZV9yZWFkc19kZgpgYGAKCmBgYHtyLCBtZXNzYWdlID0gRn0KIyBFeHBhbmQgaW52aXRybyBnZW5vdHlwZXMgYWNyb3NzIHJlYWQgc3Vic2FtcGxlIG5hbWVzCmludml0cm9fZGYgPC0gaW52aXRyb19pbXBvcnQoKQppbnZpdHJvX3JlYWRzX2RmIDwtIHN1YnNhbXBsZV9yZWFkc19kZiAlPiUgCiAgc2VsZWN0KHNhbXBsZSkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHNlcGFyYXRlKHNhbXBsZSwgaW50byA9IGMoInNhbXBsZSIsICJzdWJzYW1wbGUiKSwgc2VwID0gIl8iKSAlPiUgCiAgbGVmdF9qb2luKGludml0cm9fZGYsIGJ5ID0gInNhbXBsZSIpICU+JSAKICB1bml0ZShzYW1wbGUsIHNhbXBsZSwgc3Vic2FtcGxlLCBzZXAgPSAiXyIpICU+JSAKICBmb3JtYXRfaGxhX3RhYmxlKCkgJT4lIAogIGRyb3BfbmEoKQppbnZpdHJvX3JlYWRzX2RmCmBgYAoKYGBge3J9CiMgSW1wb3J0IHJlYWQgc3RhdGlzdGljcwpzdWJzYW1wbGVfcmVhZHNfYXJjYXNfbG9nX2RpciA8LSBzcHJpbnRmKCIlcy9hcmNhc0hMQSIsIHN1YnNhbXBsZV9yZWFkc19wYXRoKQpzdWJzYW1wbGVfcmVhZHNfYWxpZ25tZW50X3N0YXRzX2RmIDwtIGhsYV9tYXBwaW5nX3N0YXRzX2ltcG9ydChzdWJzYW1wbGVfcmVhZHNfc2FtcGxlcywgc3Vic2FtcGxlX3JlYWRzX2FyY2FzX2xvZ19kaXIpCmBgYAoKIyMjIENhbGN1bGF0ZSBhY2N1cmFjeQoKYGBge3J9CiMgQ2FsY3VsYXRlIGFjY3VyYWN5CnN1YnNhbXBsZV9yZWFkc19hY2N1cmFjeV9kZiA8LSBjb21wYXJlX2hsYSgKICBobGFfZGYgPSBiaW5kX3Jvd3MoCiAgICBzdWJzYW1wbGVfcmVhZHNfZGYsCiAgICBpbnZpdHJvX3JlYWRzX2RmCiAgKSwKICByZWZlcmVuY2UgPSAiaW52aXRybyIsIAogIG1ldGhvZCA9ICJhY2N1cmFjeSIKKQojIHNhdmVSRFMoc3Vic2FtcGxlX3JlYWRzX2FjY3VyYWN5X2RmLCAiaXNiX3N1YnNhbXBsZV9yZWFkc19hY2N1cmFjeS5SRFMiKQpgYGAKCgpgYGB7cn0KIyBDYWxjdWxhdGUgc3VjY2VzcwpzdWJzYW1wbGVfcmVhZHNfc3VjY2Vzc19kZiA8LSBjb21wYXJlX2hsYSgKICBobGFfZGYgPSBiaW5kX3Jvd3MoCiAgICBzdWJzYW1wbGVfcmVhZHNfZGYsCiAgICBpbnZpdHJvX3JlYWRzX2RmCiAgKSwKICByZWZlcmVuY2UgPSAiaW52aXRybyIsIAogIG1ldGhvZCA9ICJzdWNjZXNzIgopCiMgc2F2ZVJEUyhzdWJzYW1wbGVfcmVhZHNfc3VjY2Vzc19kZiwgImlzYl9zdWJzYW1wbGVfcmVhZHNfc3VjY2Vzcy5SRFMiKQpgYGAKCmBgYHtyfQojIENvbWJpbmUgdmFyaW91cyBkYXRhCnN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiA8LSBzdWJzYW1wbGVfcmVhZHNfYWNjdXJhY3lfZGYgJT4lIAogIGxlZnRfam9pbigKICAgIHN1YnNhbXBsZV9yZWFkc19zdWNjZXNzX2RmICU+JSBzZWxlY3Qoc2FtcGxlOmZpZWxkLCBnZW5vdHlwZXIsIHN1Y2Nlc3MgPSBhY2N1cmFjeSksCiAgICBieSA9IGMoInNhbXBsZSIsICJsb2N1cyIsICJmaWVsZCIsICJnZW5vdHlwZXIiKQogICklPiUgCiAgbGVmdF9qb2luKHN1YnNhbXBsZV9yZWFkc19hbGlnbm1lbnRfc3RhdHNfZGYsIGJ5ID0gYygic2FtcGxlIiwgImxvY3VzIikpICU+JSAKICBsZWZ0X2pvaW4oZ2Vub21lX3JlZ2lvbl9yZWZlcmVuY2UsIGJ5ID0gImxvY3VzIikgJT4lCiAgYmluZF9yb3dzKGFjY3VyYWN5X2RmICU+JSBzZWxlY3QoLW5fY2VsbHMpKSAlPiUgIyBObyBjZWxsIG51bWJlcnMgZm9yIHJlYWQgc3Vic2FtcGxlcwogIG11dGF0ZV9hdCh2YXJzKGMocmVhZHMsIGJwKSksIGFzLm51bWVyaWMpICU+JSAKICBtdXRhdGUoY292ZXJhZ2UgPSAocmVhZHMgKiA5MSkvYnApICU+JSAgIyBDYWxjdWxhdGUgY292ZXJhZ2UKICBtdXRhdGUobl9hbGxlbGVzID0gbWFwX2RibChhbGxlbGUsIGZ1bmN0aW9uKHgpIHN1bSghaXMubmEoeCkpKSkgJT4lICAjIENhbGN1bGF0ZSBuX2FsbGVsZXMKICBmaWx0ZXIoZ3JlcGwoIl5bQUJDXXxELltBQl0xIiwgbG9jdXMpICYgZ3JlcGwoIi0oQUN8QkwpIiwgc2FtcGxlKSAmIGdlbm90eXBlciAhPSAiaGxhbWluZXIiKSAKIyBzYXZlUkRTKHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiwgInN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZi5SRFMiKQojIHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiA8LSByZWFkUkRTKCJzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYuUkRTIikKYGBgCgojIyMgUmVhZHMgcGxvdHMKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0yLjUsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gImFjY3VyYWN5IikgCnBsdF9yZWFkX2FjY3VyYWN5X2FsbCA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYsIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAiYWNjdXJhY3kiKSAKcGx0X3JlYWRfYWNjdXJhY3lfYWJjIDwtIGdnX2NvdmVyYWdlKHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiAlPiUgZmlsdGVyKGdyZXBsKCJeW0FCQ10iLCBsb2N1cykpLCB4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gImFjY3VyYWN5IikgCmBgYApgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTIuNSwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYsIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAic3VjY2VzcyIpCnBsdF9yZWFkX3N1Y2Nlc3NfYWxsIDwtIGdnX2NvdmVyYWdlKHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJzdWNjZXNzIikKcGx0X3JlYWRfc3VjY2Vzc19hYmMgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmICU+JSBmaWx0ZXIoZ3JlcGwoIl5bQUJDXSIsIGxvY3VzKSksIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAic3VjY2VzcyIpIApgYGAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0zLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJuX2FsbGVsZXMiLCBmYWNldF9nZW5vdHlwZXIgPSBUKQpwbHRfcmVhZF9hbGxlbGVfYWxsIDwtIGdnX2NvdmVyYWdlKHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJuX2FsbGVsZXMiLCBmYWNldF9nZW5vdHlwZXIgPSBUKQpwbHRfcmVhZF9hbGxlbGVfYWJjIDwtIGdnX2NvdmVyYWdlKHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiAlPiUgZmlsdGVyKGdyZXBsKCJeW0FCQ10iLCBsb2N1cykpLCB4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCmBgYAotIERlZmF1bHQgYXJjYXNITEEgY3V0IG9mZiA3NSByZWFkcyAobG9nMTAgPSAxLjgpCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTMsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0Kc3Vic2FtcGxlX3JlYWRzX2NvbWJpbmVkX2RmICU+JSAKICBmaWx0ZXIoZmllbGQgPT0gImZpZWxkXzIiKSAlPiUgCiAgbXV0YXRlKG5fYWxsZWxlcyA9IGZhY3RvcihuX2FsbGVsZXMsIGxldmVscyA9IDA6MikpICU+JSAKICBnZ3Bsb3QoYWVzKHkgPSBuX2FsbGVsZXMsIHggPSBsb2cxMChjb3ZlcmFnZSkpKSsKICAjIGdlb21fdmlvbGluKHNjYWxlID0gImNvdW50IiwgYWxwaGEgPSAwLjUsICkrCiAgZ2VvbV9qaXR0ZXIoc2l6ZSA9IDAuMiwgd2lkdGggPSAwLCBoZWlnaHQgPTAuMiwgYWxwaGEgPSAwLjIpKwogICMgZ2VvbV9ib3hwbG90KHdpZHRoPTAuMSkrCiAgIyBnZ2JlZXN3YXJtOjpnZW9tX2JlZXN3YXJtKHNpemUgPSAwLjEsIGFscGhhID0gMC4xKSsKICAjIGNvb3JkX2ZsaXAoKSsKICBmYWNldF9ncmlkKGdlbm90eXBlciB+IGxvY3VzLCBzY2FsZXMgPSAiZnJlZSIpKwogIHRoZW1lX2J3KCkKYGBgCgojIFN1YnNhbXBsZSBDRUxMUwoKYGBge3J9CiMgSW1wb3J0IHByZWRpY3RlZCBnZW5vdHlwZXMKc3Vic2FtcGxlX2NlbGxzX3BhdGggPC0gIi9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9jb3ZpZC9pc2Ivc3Vic2FtcGxlX2NlbGxzIgpzdWJzYW1wbGVfY2VsbHNfc2FtcGxlcyA8LSB0aWJibGUoc2FtcGxlID0gbGlzdC5maWxlcyhzcHJpbnRmKCIlcy9hcmNhc0hMQSIsIHN1YnNhbXBsZV9jZWxsc19wYXRoKSkpICU+JSAKICBmaWx0ZXIoZ3JlcGwoIl5JTkNPViIsIHNhbXBsZSkpICU+JSAKICBzZXBhcmF0ZShzYW1wbGUsIGludG8gPSBjKCJzYW1wbGUiKSwgc2VwID0gIlxcLiIsIGV4dHJhID0gImRyb3AiKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUgCiAgcHVsbChzYW1wbGUpCnN1YnNhbXBsZV9jZWxsc19kZiA8LSBmb3JtYXRfaGxhX3RhYmxlKGNvbWJpbmVfSExBX2ltcG9ydChwYXRoID0gc3Vic2FtcGxlX2NlbGxzX3BhdGgsIHNhbXBsZXMgPSBzdWJzYW1wbGVfY2VsbHNfc2FtcGxlcykpCnN1YnNhbXBsZV9jZWxsc19kZiA8LSBzdWJzYW1wbGVfY2VsbHNfZGYgJT4lIAogIGZpbHRlcihncmVwbCgiXltBQkNdfEQuW0FCXTEiLCBsb2N1cykgJiBncmVwbCgiLShBQ3xCTClfIiwgc2FtcGxlKSkKc3Vic2FtcGxlX2NlbGxzX2RmCmBgYAoKCmBgYHtyLCBtZXNzYWdlID0gRn0KIyBFeHBhbmQgaW52aXRybyBnZW5vdHlwZXMgYWNyb3NzIHJlYWQgc3Vic2FtcGxlIG5hbWVzCmludml0cm9fZGYgPC0gaW52aXRyb19pbXBvcnQoKQppbnZpdHJvX2NlbGxzX2RmIDwtIHN1YnNhbXBsZV9jZWxsc19kZiAlPiUgCiAgc2VsZWN0KHNhbXBsZSkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHNlcGFyYXRlKHNhbXBsZSwgaW50byA9IGMoInNhbXBsZSIsICJzdWJzYW1wbGUiKSwgc2VwID0gIl8iKSAlPiUgCiAgbGVmdF9qb2luKGludml0cm9fZGYsIGJ5ID0gInNhbXBsZSIpICU+JSAKICB1bml0ZShzYW1wbGUsIHNhbXBsZSwgc3Vic2FtcGxlLCBzZXAgPSAiXyIpICU+JSAKICBmb3JtYXRfaGxhX3RhYmxlKCkgJT4lIAogIGRyb3BfbmEoKQppbnZpdHJvX2NlbGxzX2RmCmBgYAoKYGBge3J9CiMgSW1wb3J0IHJlYWQgc3RhdGlzdGljcwpzdWJzYW1wbGVfY2VsbHNfYXJjYXNfbG9nX2RpciA8LSBzcHJpbnRmKCIlcy9hcmNhc0hMQSIsIHN1YnNhbXBsZV9jZWxsc19wYXRoKQpzdWJzYW1wbGVfY2VsbHNfYWxpZ25tZW50X3N0YXRzX2RmIDwtIGhsYV9tYXBwaW5nX3N0YXRzX2ltcG9ydChzdWJzYW1wbGVfY2VsbHNfc2FtcGxlcywgc3Vic2FtcGxlX2NlbGxzX2FyY2FzX2xvZ19kaXIpCmBgYAoKYGBge3J9CiMgSW1wb3J0IGNlbGwgY291bnRzIGZvciBlYWNoIHN1YnNhbXBsZQpzdWJzYW1wbGVfY2VsbF9jb3VudHNfZGYgPC0gdGliYmxlKGxpbmUgPSBzeXN0ZW0oIndjIC1sIC9sYWJzL2toYXRyaWxhYi9zb2xvbW9uYi9jb3ZpZC9pc2Ivc3Vic2FtcGxlX2NlbGxzL2JhcmNvZGVzL0lOQ09WKiIsIGludGVybiA9IFQpKSAlPiUgCiAgc2VwYXJhdGUobGluZSwgaW50byA9IGMoIm5fY2VsbHMiLCAiZmlsZSIpLCBzZXAgPSAiIC8iKSAlPiUgCiAgZHJvcF9uYSgpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gZ3N1YigiXFwuLioiLCIiLGJhc2VuYW1lKGZpbGUpKSkgJT4lIAogIG11dGF0ZShuX2NlbGxzID0gYXMubnVtZXJpYyhuX2NlbGxzKSkgJT4lIAogIHNlbGVjdCgtZmlsZSkgJT4lIAogICMgc2VwYXJhdGUoc2FtcGxlLCBpbnRvID0gYygic2FtcGxlIiwgInN1YnNldCIpLCBzZXAgPSAiXyIpICU+JSAKICBiaW5kX3Jvd3MoY2VsbF9jb3VudHNfZGYpCnN1YnNhbXBsZV9jZWxsX2NvdW50c19kZgpgYGAKCiMjIyBDYWxjdWxhdGUgYWNjdXJhY3kKCmBgYHtyfQojIENhbGN1bGF0ZSBhY2N1cmFjeQpzdWJzYW1wbGVfY2VsbHNfYWNjdXJhY3lfZGYgPC0gY29tcGFyZV9obGEoCiAgaGxhX2RmID0gYmluZF9yb3dzKAogICAgc3Vic2FtcGxlX2NlbGxzX2RmLAogICAgaW52aXRyb19jZWxsc19kZgogICksCiAgcmVmZXJlbmNlID0gImludml0cm8iLCAKICBtZXRob2QgPSAiYWNjdXJhY3kiCikKIyBzYXZlUkRTKHN1YnNhbXBsZV9jZWxsc19hY2N1cmFjeV9kZiwgImlzYl9zdWJzYW1wbGVfY2VsbHNfYWNjdXJhY3kuUkRTIikKIyBzdWJzYW1wbGVfY2VsbHNfYWNjdXJhY3lfZGYgPC0gcmVhZFJEUygiaXNiX3N1YnNhbXBsZV9jZWxsc19hY2N1cmFjeS5SRFMiKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0ZSBzdWNjZXNzCnN1YnNhbXBsZV9jZWxsc19zdWNjZXNzX2RmIDwtIGNvbXBhcmVfaGxhKAogIGhsYV9kZiA9IGJpbmRfcm93cygKICAgIHN1YnNhbXBsZV9jZWxsc19kZiwKICAgIGludml0cm9fY2VsbHNfZGYKICApLAogIHJlZmVyZW5jZSA9ICJpbnZpdHJvIiwgCiAgbWV0aG9kID0gInN1Y2Nlc3MiCikKIyBzYXZlUkRTKHN1YnNhbXBsZV9jZWxsc19zdWNjZXNzX2RmLCAiaXNiX3N1YnNhbXBsZV9jZWxsc19zdWNjZXNzLlJEUyIpCnN1YnNhbXBsZV9jZWxsc19zdWNjZXNzX2RmIDwtIHJlYWRSRFMoImlzYl9zdWJzYW1wbGVfY2VsbHNfc3VjY2Vzcy5SRFMiKQpgYGAKCmBgYHtyfQojIENvbWJpbmUgdmFyaW91cyBkYXRhCnN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiA8LSBzdWJzYW1wbGVfY2VsbHNfYWNjdXJhY3lfZGYgJT4lIAogIGxlZnRfam9pbigKICAgIHN1YnNhbXBsZV9jZWxsc19zdWNjZXNzX2RmICU+JSBzZWxlY3Qoc2FtcGxlOmZpZWxkLCBnZW5vdHlwZXIsIHN1Y2Nlc3MgPSBhY2N1cmFjeSksCiAgICBieSA9IGMoInNhbXBsZSIsICJsb2N1cyIsICJmaWVsZCIsICJnZW5vdHlwZXIiKQogICklPiUgCiAgbGVmdF9qb2luKHN1YnNhbXBsZV9jZWxsc19hbGlnbm1lbnRfc3RhdHNfZGYsIGJ5ID0gYygic2FtcGxlIiwgImxvY3VzIikpICU+JSAKICBsZWZ0X2pvaW4oZ2Vub21lX3JlZ2lvbl9yZWZlcmVuY2UsIGJ5ID0gImxvY3VzIikgJT4lCiAgbGVmdF9qb2luKHN1YnNhbXBsZV9jZWxsX2NvdW50c19kZiwgYnkgPSAic2FtcGxlIikgJT4lCiAgYmluZF9yb3dzKGFjY3VyYWN5X2RmKSAlPiUgCiAgbXV0YXRlX2F0KHZhcnMoYyhyZWFkcywgYnApKSwgYXMubnVtZXJpYykgJT4lIAogIG11dGF0ZShjb3ZlcmFnZSA9IChyZWFkcyAqIDkxKS9icCkgJT4lIAogIG11dGF0ZShuX2FsbGVsZXMgPSBtYXBfZGJsKGFsbGVsZSwgZnVuY3Rpb24oeCkgc3VtKCFpcy5uYSh4KSkpKSAlPiUgCiAgZmlsdGVyKGdyZXBsKCJeW0FCQ118RC5bQUJdMSIsIGxvY3VzKSAmIGdyZXBsKCItKEFDfEJMKSIsIHNhbXBsZSkgJiBnZW5vdHlwZXIgIT0gImhsYW1pbmVyIikgCnNhdmVSRFMoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmLCAic3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmLlJEUyIpCmBgYAoKIyMjIENvdmVyYWdlIHBsb3RzCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTIuNSwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gImNvdmVyYWdlIiwgeV92YXIgPSAiYWNjdXJhY3kiKSAKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mi41LCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgeF92YXIgPSAiY292ZXJhZ2UiLCB5X3ZhciA9ICJzdWNjZXNzIikKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gImNvdmVyYWdlIiwgeV92YXIgPSAibl9hbGxlbGVzIiwgZmFjZXRfZ2Vub3R5cGVyID0gVCkKYGBgCgojIyMgQ2VsbCBudW1iZXIgcGxvdHMKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mi41LCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gImFjY3VyYWN5IikgCnBsdF9jZWxsX2FjY3VyYWN5X2FsbCA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpIApwbHRfY2VsbF9hY2N1cmFjeV9hYmMgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmICU+JSBmaWx0ZXIoZ3JlcGwoIl5bQUJDXSIsIGxvY3VzKSksIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpIApgYGAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0yLjUsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0KZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAic3VjY2VzcyIpIApwbHRfY2VsbF9zdWNjZXNzX2FsbCA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJzdWNjZXNzIikgCnBsdF9jZWxsX3N1Y2Nlc3NfYWJjIDwtIGdnX2NvdmVyYWdlKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiAlPiUgZmlsdGVyKGdyZXBsKCJeW0FCQ10iLCBsb2N1cykpLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAic3VjY2VzcyIpIApgYGAKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0zLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CmdnX2NvdmVyYWdlKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCnBsdF9jZWxsX2FsbGVsZV9hbGwgPC0gZ2dfY292ZXJhZ2Uoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAibl9hbGxlbGVzIiwgZmFjZXRfZ2Vub3R5cGVyID0gVCkKcGx0X2NlbGxfYWxsZWxlX2FiYyA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYgJT4lIGZpbHRlcihncmVwbCgiXltBQkNdIiwgbG9jdXMpKSwgeF92YXIgPSAibl9jZWxscyIsIHlfdmFyID0gIm5fYWxsZWxlcyIsIGZhY2V0X2dlbm90eXBlciA9IFQpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Mywgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQojIHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiAlPiUgCiMgICBtdXRhdGUocmVhZHMgPSByZWFkcy9uX2NlbGxzKSAlPiUgCiMgICBmaWx0ZXIocmVhZHMgPCAxMDApICU+JSAKIyAgIGdnX2NvdmVyYWdlKHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAiYWNjdXJhY3kiLCB4X2xvZyA9IEYpIApgYGAKCgoKCgoKCgojIENvbWJpbmVkIHBsb3RzCmBgYHtyLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNywgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGfQpub19sZWcgPC0gbGlzdCh0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKQpsZWdlbmQgPC0gY293cGxvdDo6Z2V0X2xlZ2VuZChwbHRfcmVhZF9hY2N1cmFjeV9hYmMpCnBsb3RfZ3JpZCgKICBwbG90X2dyaWQoCiAgICBwbHRfcmVhZF9hY2N1cmFjeV9hYmMgKyBub19sZWcsCiAgICBwbHRfcmVhZF9zdWNjZXNzX2FiYyArIG5vX2xlZywKICAgIHBsdF9yZWFkX2FsbGVsZV9hYmMsCiAgICBuY29sID0gMSwKICAgIGFsaWduID0gInYiLAogICAgYXhpcyA9ICJsciIsCiAgICByZWxfaGVpZ2h0cyA9IGMoMSwxLDEuMiksCiAgICBsYWJlbHMgPSBMRVRURVJTWzE6M10KICApLAogIHBsb3RfZ3JpZCgKICAgIHBsdF9jZWxsX2FjY3VyYWN5X2FiYyArIG5vX2xlZywKICAgIHBsdF9jZWxsX3N1Y2Nlc3NfYWJjICsgbm9fbGVnLAogICAgcGx0X2NlbGxfYWxsZWxlX2FiYywKICAgIG5jb2wgPSAxLAogICAgYWxpZ24gPSAidiIsCiAgICBheGlzID0gImxyIiwKICAgIHJlbF9oZWlnaHRzID0gYygxLDEsMS4yKSwKICAgIGxhYmVscyA9IExFVFRFUlNbNDo2XQogICksCiAgcGxvdF9ncmlkKAogICAgbGVnZW5kLAogICAgbGVnZW5kLAogICAgTkEsCiAgICBuY29sID0gMQogICksCiAgbnJvdyA9IDEsCiAgcmVsX3dpZHRocyA9IGMoMSwxLDAuMjUpCikKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMTgsIGZpZy5oZWlnaHQgPSA3LCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9Cm5vX2xlZyA8LSBsaXN0KHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpCmxlZ2VuZCA8LSBjb3dwbG90OjpnZXRfbGVnZW5kKHBsdF9yZWFkX2FjY3VyYWN5X2FsbCkKcGxvdF9ncmlkKAogIHBsb3RfZ3JpZCgKICAgIHBsdF9yZWFkX2FjY3VyYWN5X2FsbCArIG5vX2xlZywKICAgIHBsdF9yZWFkX3N1Y2Nlc3NfYWxsICsgbm9fbGVnLAogICAgcGx0X3JlYWRfYWxsZWxlX2FsbCwKICAgIG5jb2wgPSAxLAogICAgYWxpZ24gPSAidiIsCiAgICBheGlzID0gImxyIiwKICAgIHJlbF9oZWlnaHRzID0gYygxLDEsMS4yKSwKICAgIGxhYmVscyA9IExFVFRFUlNbMTozXQogICksCiAgcGxvdF9ncmlkKAogICAgcGx0X2NlbGxfYWNjdXJhY3lfYWxsICsgbm9fbGVnLAogICAgcGx0X2NlbGxfc3VjY2Vzc19hbGwgKyBub19sZWcsCiAgICBwbHRfY2VsbF9hbGxlbGVfYWxsLAogICAgbmNvbCA9IDEsCiAgICBhbGlnbiA9ICJ2IiwKICAgIGF4aXMgPSAibHIiLAogICAgcmVsX2hlaWdodHMgPSBjKDEsMSwxLjIpLAogICAgbGFiZWxzID0gTEVUVEVSU1s0OjZdCiAgKSwKICBwbG90X2dyaWQoCiAgICBsZWdlbmQsCiAgICBsZWdlbmQsCiAgICBOQSwKICAgIG5jb2wgPSAxCiAgKSwKICBucm93ID0gMSwKICByZWxfd2lkdGhzID0gYygxLDEsMC4yNSkKKQpgYGAKCiMjIyBNYWluIHBsb3QKCmBgYHtyfQpsb2NpIDwtIGMoIkEiLCAiQiIsICJDIiwgIkRQQjEiLCAiRFFBMSIsICJEUkIxIikKcGx0X3JlYWRfYWNjdXJhY3lfc3Vic2V0IDwtIGdnX2NvdmVyYWdlKAogIHN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiAlPiUgCiAgICBmaWx0ZXIobG9jdXMgJWluJSBsb2NpKSAlPiUgCiAgICBmaWx0ZXIoIShnZW5vdHlwZXIgPT0gInBobGF0IiAmIGxvY3VzID09ICJEUEIxIikpICU+JSAKICAgIGZpbHRlcighKGdlbm90eXBlciA9PSAib3B0aXR5cGUiICYgbG9jdXMgPT0gIkRQQjEiKSkgJT4lIAogICAgZmlsdGVyKCEoZ2Vub3R5cGVyID09ICJvcHRpdHlwZSIgJiBsb2N1cyA9PSAiRFFBMSIpKSAlPiUgCiAgICBmaWx0ZXIoIShnZW5vdHlwZXIgPT0gIm9wdGl0eXBlIiAmIGxvY3VzID09ICJEUkIxIikpLCAKICB4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gImFjY3VyYWN5IikgKwogIGZhY2V0X3dyYXAofmxvY3VzLCBucm93ID0gMikKcGx0X3JlYWRfYWNjdXJhY3lfc3Vic2V0CmBgYApgYGB7cn0Kc2F2ZV9wbG90KCJmaWd1cmVzX3BkZi92Mi81X2NvdmVyYWdlLnBkZiIsIHBsdF9yZWFkX2FjY3VyYWN5X3N1YnNldCwgYmFzZV9oZWlnaHQgPSA0LCBiYXNlX3dpZHRoID0gNikKYGBgCgpgYGB7cn0KcGx0X3JlYWRfYWxsZWxlX2FsbCA8LSBnZ19jb3ZlcmFnZShzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYgJT4lIAogICAgZmlsdGVyKGxvY3VzICVpbiUgbG9jaSkgJT4lIAogICAgZmlsdGVyKCEoZ2Vub3R5cGVyID09ICJwaGxhdCIgJiBsb2N1cyA9PSAiRFBCMSIpKSAlPiUgCiAgICBmaWx0ZXIoIShnZW5vdHlwZXIgPT0gIm9wdGl0eXBlIiAmIGxvY3VzID09ICJEUEIxIikpICU+JSAKICAgIGZpbHRlcighKGdlbm90eXBlciA9PSAib3B0aXR5cGUiICYgbG9jdXMgPT0gIkRRQTEiKSkgJT4lIAogICAgZmlsdGVyKCEoZ2Vub3R5cGVyID09ICJvcHRpdHlwZSIgJiBsb2N1cyA9PSAiRFJCMSIpKSwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJuX2FsbGVsZXMiLCBmYWNldF9nZW5vdHlwZXIgPSBUKQpwbHRfcmVhZF9hbGxlbGVfYWxsCmBgYApgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodD03fQpwbHRfbWFpbiA8LSBwbG90X2dyaWQoCiAgcGx0X3JlYWRfYWNjdXJhY3lfc3Vic2V0LAogIHBsdF9yZWFkX2FsbGVsZV9hbGwsCiAgbmNvbCA9IDEsCiAgcmVsX2hlaWdodHMgPSBjKDMsMiksCiAgYWxpZ24gPSAidiIsCiAgYXhpcyA9ICJsciIsCiAgbGFiZWxzID0gYygiQSIsICJCIikKKQpwbHRfbWFpbgpgYGAKYGBge3J9CnNhdmVfcGxvdCgiZmlndXJlc19wZGYvdjIvNV9jb3ZlcmFnZS5wZGYiLCBwbHRfbWFpbiwgYmFzZV9oZWlnaHQgPSA3LCBiYXNlX3dpZHRoID0gNykKYGBgCgojIFNsb3BlIHN0YXRzCgpgYGB7cn0KZ2V0X2xtX3N0YXRzIDwtIGZ1bmN0aW9uKGRmLCB4X3ZhciA9ICJjb3ZlcmFnZSIsIHlfdmFyID0gImFjY3VyYWN5IiwgZmllbGRfdmFyID0gImZpZWxkXzIiLCB4X2xvZyA9IFQpewogICMgSW5wdXQgY2hlY2tzCiAgYXJnX2NvbCA8LSBtYWtlQXNzZXJ0Q29sbGVjdGlvbigpCiAgYXNzZXJ0Q2hvaWNlKHlfdmFyLCBjKCJhY2N1cmFjeSIsICJzdWNjZXNzIiwgIm5fYWxsZWxlcyIpLCBhZGQgPSBhcmdfY29sKQogIGFzc2VydENob2ljZSh4X3ZhciwgYygicmVhZHMiLCAiY292ZXJhZ2UiLCAibl9jZWxscyIpLCBhZGQgPSBhcmdfY29sKQogIGFzc2VydENob2ljZShmaWVsZF92YXIsIGMoImZpZWxkXzEiLCAiZmllbGRfMiIsICJmaWVsZF8zIiksIGFkZCA9IGFyZ19jb2wpCiAgYXNzZXJ0TG9naWNhbCh4X2xvZywgYWRkID0gYXJnX2NvbCkKICBpZiAoYXJnX2NvbCRpc0VtcHR5KCk9PUYpIHttYXAoYXJnX2NvbCRnZXRNZXNzYWdlcygpLHByaW50KTtyZXBvcnRBc3NlcnRpb25zKGFyZ19jb2wpfQogIAogICMgYnJvd3NlcigpCiAgZGYgPC0gZGYgJT4lIAogICAgZmlsdGVyKGZpZWxkID09IGZpZWxkX3ZhcikgJT4lIAogICAgZXhjbHVkZV9nZW5vdHlwZXJfZmllbGRzKCkgIyBSZXBsYWNlIHcvIGV4Y2x1ZGVfZ2Vub3R5cGVyX2ZpZWxkcz8KICBpZiAoeF9sb2cgPT1UKXtkZiA8LSBkZiAlPiUgbXV0YXRlX2F0KHhfdmFyLCBsb2cxMCl9CiAgZGYgJT4lCiAgZmlsdGVyX2F0KHhfdmFyLCBmdW5jdGlvbih4KSAhaXMubmEoeCkgJiAhaXMuaW5maW5pdGUoeCkpICU+JSAKICBncm91cF9ieShsb2N1cywgZmllbGQsIGdlbm90eXBlcikgJT4lCiAgbmVzdCgpICU+JQogIG11dGF0ZShkYXRhID0gbWFwKGRhdGEsIGZ1bmN0aW9uKHgpewogICAgc3VtbWFyeShsbSghIXN5bSh5X3ZhcikgfiAhIXN5bSh4X3ZhciksIGRhdGEgPSB4KSkgJT4lIGJyb29tOjp0aWR5KCkgJT4lIGZpbHRlcih0ZXJtID09IHhfdmFyKQogIH0pKSAlPiUgdW5uZXN0X3dpZGVyKGRhdGEpICU+JQogIG11dGF0ZShlc3RfbWluID0gcm91bmQoZXN0aW1hdGUgLSAyKnN0ZC5lcnJvciwgMiksCiAgICAgICAgIGVzdF9tYXggPSByb3VuZChlc3RpbWF0ZSArIDIqc3RkLmVycm9yLCAyKSkKfQoKbW9kZWxfc3RhdHMgPC0gZ2V0X2xtX3N0YXRzKHN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiwgeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIsIHhfbG9nID0gVCkKbW9kZWxfc3RhdHMKYGBgCgpgYGB7cn0KbW9kZWxfc3RhdHMgPC0gYmluZF9yb3dzKAogIGdldF9sbV9zdGF0cyhzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYsIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAiYWNjdXJhY3kiLCB4X2xvZyA9IFQpICU+JSBtdXRhdGUoeF92YXIgPSAicmVhZHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpLAogIGdldF9sbV9zdGF0cyhzdWJzYW1wbGVfcmVhZHNfY29tYmluZWRfZGYsIHhfdmFyID0gInJlYWRzIiwgeV92YXIgPSAic3VjY2VzcyIsIHhfbG9nID0gVCkgJT4lIG11dGF0ZSh4X3ZhciA9ICJyZWFkcyIsIHlfdmFyID0gInN1Y2Nlc3MiKSwKICBnZXRfbG1fc3RhdHMoc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmLCB4X3ZhciA9ICJuX2NlbGxzIiwgeV92YXIgPSAiYWNjdXJhY3kiLCB4X2xvZyA9IFQpICU+JSBtdXRhdGUoeF92YXIgPSAiY2VsbHMiLCB5X3ZhciA9ICJhY2N1cmFjeSIpLAogIGdldF9sbV9zdGF0cyhzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYsIHhfdmFyID0gIm5fY2VsbHMiLCB5X3ZhciA9ICJzdWNjZXNzIiwgeF9sb2cgPSBUKSAlPiUgbXV0YXRlKHhfdmFyID0gImNlbGxzIiwgeV92YXIgPSAic3VjY2VzcyIpCikgICU+JSAKICAjIGZpbHRlcihncmVwbCgiXltBQkNdIiwgbG9jdXMpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBmaWx0ZXIoZmllbGQgPT0gImZpZWxkXzIiKSAKYGBgCgoKCiMgVGFibGUKYGBge3J9CmNvbF92YXJzIDwtIGMoInhfdmFyIiwgImxvY3VzIikKcm93X3ZhcnMgPC0gYygieV92YXIiLCAiZ2Vub3R5cGVyIikKCmZsZXhfZGYgPC0gbW9kZWxfc3RhdHMgJT4lIAogICMgZmlsdGVyKGdyZXBsKCJeW0FCQ10iLCBsb2N1cykpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGZpbHRlcihmaWVsZCA9PSAiZmllbGRfMiIpICU+JSAKICBtdXRhdGUoQ0kgPSBzcHJpbnRmKCIoJXMpIC0gKCVzKSIsIGVzdF9taW4sIGVzdF9tYXgpKSAlPiUgCiAgbXV0YXRlKGdlbm90eXBlciA9IHJlZm9ybWF0X2hsYV9nZW5vdHlwZXIoZ2Vub3R5cGVyKSkgJT4lIAogIHNlbGVjdChnZW5vdHlwZXIsIGxvY3VzLCBDSSwgeF92YXIsIHlfdmFyKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGNvbF92YXJzLCB2YWx1ZXNfZnJvbSA9ICJDSSIsIG5hbWVzX3NlcCA9ICAiLSIpIAoKZGZfa2V5IDwtIHRpYmJsZShjb2xfa2V5cyA9IG5hbWVzKGZsZXhfZGYpKSAlPiUgCiAgZmlsdGVyKCEoY29sX2tleXMgJWluJSBjKGNvbF92YXJzLCByb3dfdmFycykpKSAlPiUgCiAgc2VwYXJhdGUoY29sX2tleXMsIGludG8gPSBjb2xfdmFycywgc2VwID0gIi0iLCByZW1vdmUgPSBGKSAlPiUKICBtdXRhdGVfYXQodmFycyhjb250YWlucygiZ2Vub3R5cGVyIikpLCByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKSAlPiUgCiAgYXJyYW5nZV9hdChjb2xfdmFycykgJT4lIAogIG11dGF0ZV9hbGwoYXMuY2hhcmFjdGVyKSAlPiUgCiAgbXV0YXRlX2F0KGNvbF92YXJzWyEoY29sX3ZhcnMgJWluJSAibG9jdXMiKV0sIHN0cl90b19zZW50ZW5jZSkKCmZsZXhfZGYgJT4lIAogIHNlbGVjdChyb3dfdmFycywgZXZlcnl0aGluZygpKSAlPiUgCiAgbXV0YXRlX2F0KHJvd192YXJzWyEocm93X3ZhcnMgJWluJSAiZ2Vub3R5cGVyIildLCBzdHJfdG9fc2VudGVuY2UpICU+JSAKICAjIHNlbGVjdChsb2N1cywgZGZfa2V5JGNvbF9rZXlzKSAlPiUgCiAgZmxleHRhYmxlKCkgJT4lIAogIGNvbGZvcm1hdF9jaGFyKG5hX3N0ciA9ICItLS0iKSAlPiUKICBtZXJnZV92KGo9MToyKSAlPiUgCiAgc2V0X2hlYWRlcl9kZihtYXBwaW5nID0gZGZfa2V5LCBrZXkgPSAiY29sX2tleXMiKSAlPiUgCiAgbWVyZ2VfaChwYXJ0ID0gImhlYWRlciIpICU+JQogIHRoZW1lX3ZhbmlsbGEoKSAlPiUgCiAgIyB2bGluZShqPXZsaW5lc19zZXF1ZW5jZSwgYm9yZGVyID0gZnBfYm9yZGVyX2RlZmF1bHQoKSkgJT4lCiAgZml4X2JvcmRlcl9pc3N1ZXMoKSAlPiUgCiAgYXV0b2ZpdCgpICU+JSAKICBmb250c2l6ZShzaXplID0gOCwgcGFydCA9ICJhbGwiKSAlPiUgCiAgcHJpbnQocHJldmlldyA9ICJwcHR4IikKYGBgCgoKYGBge3J9CiAgZGYgPC0gZGYgJT4lIAogICAgbXV0YXRlKGZpZWxkID0gcmVmb3JtYXRfaGxhX2ZpZWxkKGZpZWxkKSkgJT4lIAogICAgbXV0YXRlX2F0KHZhcnMoY29udGFpbnMoImdlbm90eXBlciIpKSwgcmVmb3JtYXRfaGxhX2dlbm90eXBlcikgJT4lIAogICAgbXV0YXRlKGNlbGxfdmFsdWUgPSBpZmVsc2UoIWlzLm5hKHNlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKCIlcyAlcyAlcyIsIHJvdW5kKG1lYW5fYWNjdXJhY3ksMiksIlx1MDBCMSIscm91bmQoc2UsMikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ByaW50ZigiJXMiLCByb3VuZChtZWFuX2FjY3VyYWN5LDIpKSkpICU+JQogICAgbXV0YXRlKGNlbGxfdmFsdWUgPSBpZmVsc2UoZ3JlcGwoIk5BIiwgY2VsbF92YWx1ZSksIE5BLCBjZWxsX3ZhbHVlKSkgJT4lIAogICAgc2VsZWN0KC1zZCwtc2UsIC1tZWFuX2FjY3VyYWN5KSAlPiUgCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbmVzdGluZ192YXJzLCB2YWx1ZXNfZnJvbSA9ICJjZWxsX3ZhbHVlIiwgbmFtZXNfc2VwID0gIi0iKSAKICAKICBkZl9rZXkgPC0gc3VwcHJlc3NXYXJuaW5ncyh7dGliYmxlKGNvbF9rZXlzID0gbmFtZXMoZGYpKSAlPiUgCiAgICAgIGZpbHRlcighZ3JlcGwoImxvY3VzIiwgY29sX2tleXMpKSAlPiUgCiAgICAgIHNlcGFyYXRlKGNvbF9rZXlzLCBpbnRvID0gbmVzdGluZ192YXJzLCBzZXAgPSAiLSIsIHJlbW92ZSA9IEYpICU+JQogICAgICBtdXRhdGVfYXQodmFycyhjb250YWlucygiZ2Vub3R5cGVyIikpLCByZWZvcm1hdF9obGFfZ2Vub3R5cGVyKSAlPiUgCiAgICAgIGFycmFuZ2VfYXQobmVzdGluZ192YXJzKSAlPiUgCiAgICAgIG11dGF0ZV9hbGwoYXMuY2hhcmFjdGVyKQogIH0pCiAgCiAgdmxpbmVfdmFyIDwtIHRhaWwobmVzdGluZ192YXJzLDEpCiAgdmxpbmVzX3NlcXVlbmNlIDwtIHNlcSgxLG5yb3coZGZfa2V5KSxieSA9IGxlbmd0aCh1bmlxdWUoZGZfa2V5W1t2bGluZV92YXJdXSkpKQogIGRmIDwtIGRmICU+JSAKICAgIHNlbGVjdChsb2N1cywgZGZfa2V5JGNvbF9rZXlzKSAlPiUgCiAgICBmbGV4dGFibGUoKSAlPiUgCiAgICBjb2xmb3JtYXRfY2hhcihuYV9zdHIgPSAiLS0tIikgJT4lIAogICAgc2V0X2hlYWRlcl9kZihtYXBwaW5nID0gZGZfa2V5LCBrZXkgPSAiY29sX2tleXMiKSAlPiUgCiAgICBtZXJnZV9oKHBhcnQgPSAiaGVhZGVyIikgJT4lIAogICAgdGhlbWVfdmFuaWxsYSgpICU+JSAKICAgIHZsaW5lKGo9dmxpbmVzX3NlcXVlbmNlLCBib3JkZXIgPSBmcF9ib3JkZXJfZGVmYXVsdCgpKSAlPiUKICAgIGZpeF9ib3JkZXJfaXNzdWVzKCkKICByZXR1cm4oZGYpCmBgYAoKCgoKCiMgU2NyYXRjaCB3b3JrCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTMsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0Kc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmICU+JSAKICBmaWx0ZXIoZmllbGQgPT0gImZpZWxkXzIiKSAlPiUgCiAgbXV0YXRlKG5fYWxsZWxlcyA9IGZhY3RvcihuX2FsbGVsZXMsIGxldmVscyA9IDA6MikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBuX2FsbGVsZXMsIHkgPSBsb2cxMChuX2NlbGxzKSkpKwogIGdlb21fdmlvbGluKHNjYWxlID0gIndpZHRoIikrCiAgZ2VvbV9qaXR0ZXIoc2l6ZSA9IDAuMiwgaGVpZ2h0ID0gMCwgd2lkdGggPTAuMDUsIGFscGhhID0gMC4yKSsKICAjIGdlb21fYm94cGxvdCh3aWR0aD0wLjEpKwogICMgZ2diZWVzd2FybTo6Z2VvbV9iZWVzd2FybShzaXplID0gMC4xLCBhbHBoYSA9IDAuMSkrCiAgY29vcmRfZmxpcCgpKwogIGZhY2V0X2dyaWQoZ2Vub3R5cGVyIH4gbG9jdXMsIHNjYWxlcyA9ICJmcmVlIikrCiAgdGhlbWVfYncoKQpgYGAKIyBDb3ZlcmFnZSBzdGF0cwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTMsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0Kc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBuX2NlbGxzLCB5ID0gY292ZXJhZ2UsIGNvbG9yID0gZ2Vub3R5cGVyKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGZhY2V0X3dyYXAoZ2Vub3R5cGVyIH4gbG9jdXMsIHNjYWxlcyA9ICJmcmVlIiwgbnJvdyA9IDMpICsgCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD0zLCB3YXJuaW5nID0gRiwgbWVzc2FnZSA9IEZ9CnN1YnNhbXBsZV9jZWxsc19jb21iaW5lZF9kZiAlPiUgCiAgc2VsZWN0KC1nZW5vdHlwZXIpICU+JSBkaXN0aW5jdCgpICU+JSAKICBtdXRhdGUoY292X3Blcl9jZWxsID0gY292ZXJhZ2Uvbl9jZWxscykgJT4lIAogIGdncGxvdChhZXMoeCA9IGxvZzEwKG5fY2VsbHMpLCB5ID0gY292X3Blcl9jZWxsKSkgKyAKICBnZW9tX3BvaW50KGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjEpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKSsKICAjIHN0YXRfc21vb3RoKCkrCiAgICBmYWNldF93cmFwKCB+IGxvY3VzLCBzY2FsZXMgPSAiZnJlZSIsIG5yb3cgPSAxKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkrCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9Niwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQpzdWJzYW1wbGVfY2VsbHNfY29tYmluZWRfZGYgJT4lIAogIHNlbGVjdCgtZ2Vub3R5cGVyKSAlPiUgZGlzdGluY3QoKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShuX2NlbGxzKSkgJT4lIAogIG11dGF0ZShjb3ZfcGVyX2NlbGwgPSBjb3ZlcmFnZS9uX2NlbGxzKSAlPiUgCiAgbXV0YXRlKG5fY2VsbHMgPSBjdXQobl9jZWxscywgc2VxKDAsMTAwMDAsYnk9MjUwMCksIHJpZ2h0ID0gRikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBuX2NlbGxzLCB5ID0gY292X3Blcl9jZWxsKSkgKyAKICBzdGF0X3N1bW1hcnkoZnVuID0gZnVuY3Rpb24oeCkgbWVhbih4LG5hLnJtPVQpLCBnZW9tID0gImJhciIpICsKICAjIGdlb21fcG9pbnQoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuMSkgKwogICMgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIikrCiAgIyBzdGF0X3Ntb290aCgpKwogIGZhY2V0X3dyYXAoIH4gbG9jdXMsIHNjYWxlcyA9ICJmcmVlIiwgbnJvdyA9IDEpICsKICB0aGVtZV9idygpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpCmBgYAoKYGBge3J9Cmhpc3QoY3V0KDE6MTAwLCBzZXEoMSwxMDAsMTApKSkKYGBgCgpgYGB7cn0Kc3Vic2FtcGxlX2NlbGxzX2NvbWJpbmVkX2RmICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGNvdW50KGdlbm90eXBlciwgbG9jdXMsIGZpZWxkKQpgYGAKYGBge3J9CnN1YnNhbXBsZV9yZWFkc19jb21iaW5lZF9kZiAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBjb3VudChnZW5vdHlwZXIsIGxvY3VzLCBmaWVsZCkKYGBgCg==